# -- coding: utf-8 --
"""
# MIAHX© 交互框架信息表示模型的 FastDDS 参与者

**模块：MIAHX© / 框架基础服务层（FFST） / 交互框架信息表示模型（IFIRM）**

Version: 0.4.10
Last updated: October 2, 2024

## Contributor(s)
* YAO Naiming <[naiming@iscas.ac.cn](mailto:naiming@iscas.ac.cn)>

## Change log
### v0.4.10
-.2: [优化] 日志
### v0.4.7
-.24: [修复] 新增 RLock 锁，修复跨线程 add_topic, add_publisher, add_subscriber 时的同步缺陷
-.24: [修复] get_topic 返回值为字典时的处理缺陷
-.24: [新增] add_topic, add_publisher, add_subscriber, get_topic 方法的返回值类型
### v0.4.5
-.21: [新增] 初始实现

---
Copyright © 2024-2025 MIAHX Contributors, ISCAS, and ASIC. All Rights Reserved.
Licensed under Apache-2.0.
"""

import os
import threading
import typing

import fastdds
from loguru import logger

_log = logger.bind(module_name=os.path.basename(__file__))


class Domains:
    MIAHX = 57


class ParticipantQosTypes:
    DEFAULT = 0,
    LARGE_DATA = 1


class PublisherQosTypes:
    DEFAULT = 0,


class SubscriberQosTypes:
    DEFAULT = 0,


class Participant:
    def __init__(self, domain=Domains.MIAHX, qos_type=ParticipantQosTypes.DEFAULT):
        """
        创建 DDS 应用程序的参与者。
        当 qos_type = ParticipantQosTypes.LARGE_DATA 时，将通过加载 large_data_profile.xml 初始化参与者。

        :param domain: 领域编号，范围 (0, 200)
        :param qos_type: 参与者类型，由 ParticipantQosTypes 类定义
        """
        if domain < 0 or domain > 200:
            raise ValueError('domain')

        self.domain: int = domain

        factory = fastdds.DomainParticipantFactory.get_instance()
        self.pqos = fastdds.DomainParticipantQos()

        if qos_type == ParticipantQosTypes.LARGE_DATA:
            # 大数据模式，可加速大数据传输，但尚未真正满足需要，例如仍然不能支持6MB图像文件以30FPS发送
            profile_file = os.path.join(os.path.dirname(__file__), 'large_data_profile.xml')
            factory.load_XML_profiles_file(profile_file)
            self.participant = factory.create_participant_with_profile(domain, 'DisableBuiltinTransportsParticipant')
            self.participant.get_qos(self.pqos)
            _log.info(f'已加载 [{profile_file}]')
        else:
            # 默认模式，socket 缓冲大小约 65.5 kb
            factory.get_default_participant_qos(self.pqos)
            self.participant = factory.create_participant(domain, self.pqos)

        self._lock = threading.RLock()
        self._topics = {}
        self._pubs = {}
        self._subs = {}

    def __del__(self):
        # 释放底层 swig C++ 资源
        factory = fastdds.DomainParticipantFactory.get_instance()
        self.participant.delete_contained_entities()
        factory.delete_participant(self.participant)

    def add_topic(self, topic_name: str, topic_data_type) -> fastdds.Topic:
        """
        创建并返回 Topic，当且仅当该 topic_name 未创建；否则，返回已有 topic 对象

        :param topic_name: 主题名称，由 Topics 类定义
        :param topic_data_type: 主题数据类型，由 IDL 定义并包装为 Python 类型
        :return: FastDDS Topic 对象
        """
        if not topic_name:
            raise ValueError('topic_name')

        if topic_data_type is None:
            raise ValueError('topic_data_type')

        with self._lock:
            if topic_name in self._topics:
                return self._topics.get(topic_name).get('topic')
            type_support = fastdds.TypeSupport(topic_data_type)
            self.participant.register_type(type_support)
            topic_qos = fastdds.TopicQos()
            topic = self.participant.create_topic(topic_name, topic_data_type.getName(), topic_qos)
            self._topics[topic_name] = {
                'topic_data_type': topic_data_type,
                'type_support': type_support,
                'topic_qos': topic_qos,
                'topic': topic
            }
            return topic

    def add_publisher(self, qos_type=PublisherQosTypes.DEFAULT) -> fastdds.Publisher:
        """
        创建并返回 Publisher，当且仅当该 qos_type 未创建；否则，返回已有 Publisher 对象

        :param qos_type: 发布者类型，由 PublisherQosTypes 类定义
        :return: FastDDS Publisher 对象
        """
        with self._lock:
            if qos_type in self._pubs:
                return self._pubs.get(qos_type)
            publisher_qos = fastdds.PublisherQos()
            self.participant.get_default_publisher_qos(publisher_qos)
            # if qos_type == PublisherQosTypes.?:
            #     pass
            publisher = self.participant.create_publisher(publisher_qos)
            self._pubs[qos_type] = publisher
            return publisher

    def add_subscriber(self, qos_type=SubscriberQosTypes.DEFAULT) -> fastdds.Subscriber:
        """
        创建并返回 Subscriber，当且仅当该 qos_type 未创建；否则，返回已有 Subscriber 对象

        :param qos_type: 发布者类型，由 SubscriberQosTypes 类定义
        :return: FastDDS Subscriber 对象
        """
        with self._lock:
            if qos_type in self._subs:
                return self._subs.get(qos_type)
            subscriber_qos = fastdds.SubscriberQos()
            self.participant.get_default_subscriber_qos(subscriber_qos)
            # if qos_type == SubscriberQosTypes.?:
            #     pass
            subscriber = self.participant.create_subscriber(subscriber_qos)
            self._subs[qos_type] = subscriber
            return subscriber

    def get_topic(self, topic_name: str) -> typing.Optional[dict]:
        return self._topics.get(topic_name, None)

    def get_publisher(self, qos_type=PublisherQosTypes.DEFAULT):
        return self._pubs.get(qos_type, None)

    def get_subscriber(self, qos_type=SubscriberQosTypes.DEFAULT):
        return self._subs.get(qos_type, None)
